package cn.ht.volatiledemo;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class MyData{
    AtomicInteger number = new AtomicInteger(0);
    //保证资源可见性
//    volatile int number = 0;
    //不保证资源可见性
//    int number = 0;
    //定义资源类，让number的值改为60，验证其他线程是否可以看见此变化
//    public void addTo60(){
//        this.number = 60;
//    }
    //不保证原子性
    public void addPlus(){
//        number.incrementAndGet();
        number.getAndIncrement();
    }
    //保证原子性方案：1,加synchronized
//    public synchronized void addPlus(){
//        number++;
//    }
}

/**
 * @author ht
 * @date 2020-5-20
 *  1.验证volatile的可见性
 *      1.1 加入int number = 0；number变量之前根本没有添加volatile关键字修饰
 *      1.2 添加了volatile，可以解决可见性问题。
 *  2.验证volatile不保证原子性
 *      2.1 原子性指的是什么意思?
 *          不可分割，完整性，也即某个线程正在做某个业务时，中间不可以被加塞或者被分割，需要整体完整，要么同时成功，要么同时失败
 *      2.2 volatile是否可以保证原子性？ - no
 *      2.3 why?
 *      2.4 如何解决原子性？
 *          a. 资源类用synchronized修饰
 *          b. AtomicInteger
 */
public class VolatileDemo {

    public static void main(String[] args) {
        MyData myData = new MyData();
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000; j++) {
                    myData.addPlus();
                }
            },String.valueOf(i)).start();
        }

        //需要等待上面20个线程全部计算成功之后，得用main线程获取最终的结果值是多少？
        //判断当前线程除了main线程和GC线程之外还有没有上面新建的线程
        while(Thread.activeCount()>2){
            //如果还有自己新建的线程，则将线程的执行权交出去。
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"\t finally number values:"+myData.number);
    }

    /**
     * 功能描述 - volatile 可以保证可见性，即使通知其它线程，主物理内存的值已经被修改
     * @author ht
     * @date 2020-5-20
     * @param
     * @return void
     */
    private static void seeOkByVolatile() {
        //资源类
        MyData myData = new MyData();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() +"\t come in");
            //让线程暂停一会
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
//            myData.addTo60();
            System.out.println(Thread.currentThread().getName() +"\t 修改之后的值为："+myData.number);
        },"AAA").start();

        //第二个线程就是main线程
//        while(myData.number==0){
//            //如果number的值有被修改，且被main线程感知到，就跳出循环
//        }
        System.out.println(Thread.currentThread().getName() +"\t 完成！，main线程获取到的值是："+myData.number);
    }
}
